#!/php -q
<?php  /*  >php -q server.php  */  error_reporting(E_ALL); set_time_limit(0); ob_implicit_flush();  $master  = WebSocket("localhost",12345); $sockets = array($master); $users   = array(); $debug   = false;  while(true){   
	$changed = $sockets;   socket_select($changed,$write=NULL,$except=NULL,NULL);   foreach($changed as $socket){
		if($socket==$master){
			$client=socket_accept($master);       if($client<0){
				console("socket_accept() failed"); continue;
			}       else{ connect($client);
			}
		}     else{       $bytes = @socket_recv($socket,$buffer,2048,0);       if($bytes==0){
			disconnect($socket);
		}       else{         $user = getuserbysocket($socket);         if(!$user->handshake){
			dohandshake($user,$buffer);
		}         else{ process($user,$buffer);
		}
		}
		}
	}
}  //---------------------------------------------------------------
function process($user,$msg){
	$action = unwrap($msg);   say("< ".$action);   switch($action){
		case "hello" : send($user->socket,"hello human");                       break;     case "hi"    : send($user->socket,"zup human");                         break;     case "name"  : send($user->socket,"my name is Multivac, silly I know"); break;     case "age"   : send($user->socket,"I am older than time itself");       break;     case "date"  : send($user->socket,"today is ".date("Y.m.d"));           break;     case "time"  : send($user->socket,"server time is ".date("H:i:s"));     break;     case "thanks": send($user->socket,"you're welcome");                    break;     case "bye"   : send($user->socket,"bye");                               break;     default      : send($user->socket,$action." not understood");           break;
	}
}  function send($client,$msg){
	say("> ".$msg);   $msg = wrap($msg);   socket_write($client,$msg,strlen($msg));
}  function WebSocket($address,$port){
	$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");   socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");   socket_bind($master, $address, $port)                    or die("socket_bind() failed");   socket_listen($master,20)                                or die("socket_listen() failed");   echo "Server Started : ".date('Y-m-d H:i:s')."\n";   echo "Master socket  : ".$master."\n";   echo "Listening on   : ".$address." port ".$port."\n\n";   return $master;
}  function connect($socket){
	global $sockets,$users;   $user = new User();   $user->id = uniqid();   $user->socket = $socket;   array_push($users,$user);   array_push($sockets,$socket);   console($socket." CONNECTED!");
}  function disconnect($socket){
	global $sockets,$users;   $found=null;   $n=count($users);   for($i=0;$i<$n;$i++){
		if($users[$i]->socket==$socket){
			$found=$i; break;
		}
	}   if(!is_null($found)){
		array_splice($users,$found,1);
	}   $index = array_search($socket,$sockets);   socket_close($socket);   console($socket." DISCONNECTED!");   if($index>=0){
		array_splice($sockets,$index,1);
	}
}  function dohandshake($user,$buffer){
	console("\nRequesting handshake...");   console($buffer);   list($resource,$host,$origin,$strkey1,$strkey2,$data) = getheaders($buffer);   console("Handshaking...");    $pattern = '/[^\d]*/';   $replacement = '';   $numkey1 = preg_replace($pattern, $replacement, $strkey1);   $numkey2 = preg_replace($pattern, $replacement, $strkey2);    $pattern = '/[^ ]*/';   $replacement = '';   $spaces1 = strlen(preg_replace($pattern, $replacement, $strkey1));   $spaces2 = strlen(preg_replace($pattern, $replacement, $strkey2));    if ($spaces1 == 0 || $spaces2 == 0 || $numkey1 % $spaces1 != 0 || $numkey2 % $spaces2 != 0) {
		socket_close($user->socket);         console('failed');         return false;
	}    $ctx = hash_init('md5');   hash_update($ctx, pack("N", $numkey1/$spaces1));   hash_update($ctx, pack("N", $numkey2/$spaces2));   hash_update($ctx, $data);   $hash_data = hash_final($ctx,true);    $upgrade  = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" .               "Upgrade: WebSocket\r\n" .               "Connection: Upgrade\r\n" .               "Sec-WebSocket-Origin: " . $origin . "\r\n" .               "Sec-WebSocket-Location: ws://" . $host . $resource . "\r\n" .               "\r\n" .               $hash_data;    socket_write($user->socket,$upgrade.chr(0),strlen($upgrade.chr(0)));   $user->handshake=true;   console($upgrade);   console("Done handshaking...");   return true;
}  function getheaders($req){
	$r=$h=$o=null;   if(preg_match("/GET (.*) HTTP/"   ,$req,$match)){
		$r=$match[1];
	}   if(preg_match("/Host: (.*)\r\n/"  ,$req,$match)){
		$h=$match[1];
	}   if(preg_match("/Origin: (.*)\r\n/",$req,$match)){
		$o=$match[1];
	}   if(preg_match("/Sec-WebSocket-Key2: (.*)\r\n/",$req,$match)){
		$key2=$match[1];
	}   if(preg_match("/Sec-WebSocket-Key1: (.*)\r\n/",$req,$match)){
		$key1=$match[1];
	}   if(preg_match("/\r\n(.*?)\$/",$req,$match)){
		$data=$match[1];
	}   return array($r,$h,$o,$key1,$key2,$data);
}  function getuserbysocket($socket){
	global $users;   $found=null;   foreach($users as $user){
		if($user->socket==$socket){
			$found=$user; break;
		}
	}   return $found;
}  function     say($msg=""){
	echo $msg."\n";
} function    wrap($msg=""){
	return chr(0).$msg.chr(255);
} function  unwrap($msg=""){
	return substr($msg,1,strlen($msg)-2);
} function console($msg=""){
	global $debug; if($debug){
		echo $msg."\n";
	}
}  class User{   var $id;   var $socket;   var $handshake;
}  ?>